from game_agent import GameAgent
from time import clock
from math import sqrt
from extended_alpha_beta import ExtendedAlphaBetaSearch, DEBUG, INFINITY
from loa_game import DIRECTIONS

class Smith(GameAgent):
    def move(self, game_state):
        return self.alphaBeta.search(game_state, clock())
    
    def setup(self, player, game_state, turn_time_limit, setup_time_limit):
        self.player = player
        u = lambda state: self.utility(state, player)
        self.alphaBeta = ExtendedAlphaBetaSearch(self.player, 2, u, turn_time_limit, game_state)

    def utility(self, state, player):
        '''
        Utility function for board assessment.
        
        @param state: the state to be evaluated.
        @param player: the color of the current player
        '''        
        winner = state.getWinner()
        if player == winner: return INFINITY
        elif not (None == winner or "TIE" == winner): return -INFINITY
        
        i = 0
        quad2Count = 0
        quad3Count = 0
        quad4Count = 0
        quad2List = []
        quad3List = []
        quad4List = []
        quad2Bonus = 5
        quad3Bonus = 10
        quad4Bonus = 15
        xSum = 0
        ySum = 0
        numOfPieces = 0
        self.checkTime()
        while i < state.size:
            j = 0
            while j < state.size:
                #Quad - Always from upper-right corner
                if i+1 < state.size and j+1 < state.size:
                    count = (player == state.board[i][j])+\
                            (player == state.board[i+1][j])+\
                            (player == state.board[i][j+1])+\
                            (player == state.board[i+1][j+1])
                    diagCount = ((player == state.board[i][j])+\
                            (player == state.board[i+1][j+1]))\
                            or \
                            ((player == state.board[i+1][j])+\
                            (player == state.board[i][j+1]))
                if 3 == count:
                    quad3Count += 1
                    quad3List.append((i+0.5,j+0.5))
                elif 4 == count:
                    quad4Count += 1
                    quad4List.append((i+0.5,j+0.5))
                elif 2 == diagCount:
                    quad2Count += 1
                    quad2List.append((i+0.5,j+0.5))
                if state.board[i][j] == player:
                    numOfPieces += 1
                    xSum += i;
                    ySum += j;                
                else:
                    pass
                j += 1
                self.checkTime()
            i += 1
            self.checkTime()
        #Center of Mass Coordinate
        avgX = float(xSum) / numOfPieces
        avgY = float(ySum) / numOfPieces
        quadRank = 0
        self.checkTime()
        #rank quads
        for q in quad2List:
            quadRank += quad2Bonus/(1+sqrt(abs(q[0]-avgX)+abs(q[1]-avgY)))        
        for q in quad3List:
            quadRank += quad3Bonus/(1+sqrt(abs(q[0]-avgX)+abs(q[1]-avgY)))
        for q in quad4List:
            quadRank += quad4Bonus/(1+sqrt(abs(q[0]-avgX)+abs(q[1]-avgY)))
        
        #myVal = 1/((myMaxX-myMinX+1)*(myMaxY-myMinY+1))
        #opVal = 1/((opMaxX-opMinX+1)*(opMaxY-opMinY+1))
        if not hasattr(state, "candidate"):
            if state.getCurrentPlayer() == player:
                myVal = state.maxEvaluator + quadRank
            else:
                myVal = state.minEvaluator + quadRank
        else:
            self.checkTime()
            if state.getCurrentPlayer() == player:
                myVal = state.maxEvaluator + quadRank + 5*self.getConnectiveComponent(state, state.candidate[0], state.candidate[1], player)
            else:
                myVal = state.minEvaluator + quadRank + 5*self.getConnectiveComponent(state, state.candidate[0], state.candidate[1], player)
        # opVal = (opMaxX-opMinX+1)*(opMaxY-opMinY+1)
        return myVal
        return 1
    
    def getConnectiveComponent(self, state, initial_row, initial_col, player):
        '''
        Performs BFS to traverse the connective component, beginning at the given coordinate.
        
        @param initial_row: The initial row coordinate.
        @param initial_col: The initial column coordinate.
        @param player: The player who's connective component we would like to traverse.
        @return: The closed set - the connective component.
        '''
        open = set()
        open.add((initial_row, initial_col))
        closed = set()
        
        while len(open) > 0:
            (row, col) = open.pop()
            closed.add((row, col))
            for direction in DIRECTIONS:
                (neighbor_row, neighbor_col) = direction.move((row, col))
                if (0 <= neighbor_row < state.size) and (0 <= neighbor_col < state.size) \
                    and (state.board[neighbor_row][neighbor_col] == player) \
                    and ((neighbor_row, neighbor_col) not in closed):
                    open.add((neighbor_row, neighbor_col))
        
        return len(closed)   
    def checkTime(self):
        if self.alphaBeta.endTime-clock() <= 0:
            return self.alphaBeta.best_action